import * as path from "path";
import * as fs from "fs";
import * as tls from "tls";
import configuration from "./common/configuration";
import { NestFactory } from "@nestjs/core";
import {
  FastifyAdapter,
  NestFastifyApplication,
} from "@nestjs/platform-fastify";
import { AppModule } from "./app.module";
import { RequestMethod, ValidationPipe } from "@nestjs/common";
import { TimeInterceptor } from "./interceptor/time.interceptor";
import { IPInterceptor } from "./interceptor/ip.interceptor";
import { PageViewService } from "./page/view.service";

import fastifyMultipart from "@fastify/multipart";
import fastifyCookie from "@fastify/cookie";
import fastifySession from "@fastify/session";

// 异常捕获
process.on("uncaughtException", (e) => {
  console.error(e);
  console.info(e.stack);
});

let app: NestFastifyApplication = undefined;
const pageViewService: PageViewService = new PageViewService();

async function bootstrap() {
  const secureContext = {};
  if (configuration.server.https && configuration.server.https.enable) {
    if (
      !configuration.server.https.certs ||
      configuration.server.https.certs.length === 0
    ) {
      throw new Error("HTTPS证书配置错误");
    }

    for (const certInfo of configuration.server.https.certs) {
      if (!certInfo.servername) continue;

      if (certInfo.cert.startsWith("./")) {
        certInfo.cert = path.join(__dirname, certInfo.cert);
      } else if (certInfo.cert.startsWith("../")) {
        certInfo.cert = path.join(__dirname, "../", certInfo.cert);
      }
      if (certInfo.key.startsWith("./")) {
        certInfo.key = path.join(__dirname, certInfo.key);
      } else if (certInfo.key.startsWith("../")) {
        certInfo.key = path.join(__dirname, "../", certInfo.key);
      }

      for (const servername of certInfo.servername) {
        secureContext[servername] = tls.createSecureContext({
          cert: fs.readFileSync(certInfo.cert),
          key: fs.readFileSync(certInfo.key),
        });
      }
    }
  }

  // HTTP OPTIONS
  const httpsOptions = {
    allowHTTP1: true,
    SNICallback(
      servername: string,
      cb: (err: Error | null, ctx?: tls.SecureContext) => void,
    ) {
      console.log("servername:", servername, secureContext[servername]);
      if (secureContext[servername]) {
        cb(null, secureContext[servername]);
      } else {
        if (secureContext["default"]) {
          cb(null, secureContext["default"]);
        } else {
          cb(new Error("no cert for " + servername));
        }
      }
    },
  };
  const fastifyHttpOptions: any = (function () {
    return configuration.server.https?.enable
      ? {
          http2: configuration.server.https?.http2,
          https: httpsOptions,
        }
      : {};
  })();

  app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(fastifyHttpOptions),
  );
  const staticResourcePath = path.join(__dirname, "../public");
  app.setGlobalPrefix("api", {
    include: [
      "/resource/max/size/file",
      "/resource/max_size/file",
      "/resource/max-size/file",
      "/resource/maxSize/file",
      "/resource/max/size/chunk",
      "/resource/max_size/chunk",
      "/resource/max-size/chunk",
      "/resource/maxSize/chunk",
    ],
    exclude: [
      // avatar
      "/avatar/qq/:qq",
      "/avatar/email/:email",
      "/avatar/mail/:email",
      "/avatar/:uid",
      "/avatar/user/:uid",
      "/avatar/cravatar/:uid",
      // resource
      {
        path: "/resource/:type/:name",
        method: RequestMethod.GET,
      },
      {
        path: "/resource/:type/:uid/:name",
        method: RequestMethod.GET,
      },
      {
        path: "/resource/self/:type/:name",
        method: RequestMethod.GET,
      },
      {
        path: "/resource/:type/self/:name",
        method: RequestMethod.GET,
      },
      // article html
      "/page/article/:id",
      "/pages/article/:id",
      "/ssr/article/:id",
      "/rss",
    ],
  });
  app.useStaticAssets({
    root: staticResourcePath,
    allowedPath(_pathName, _root, request) {
      pageViewService.createPageView(request);
      return true;
    },
  });
  app.useGlobalPipes(new ValidationPipe());
  app.useGlobalInterceptors(new TimeInterceptor(), new IPInterceptor());
  app.use((_request, response, next) => {
    response.setHeader("Accept-CH", "Sec-CH-UA-Platform-Version");
    next();
  });

  const fastifyInstance = app.getHttpAdapter().getInstance();
  fastifyInstance.register(fastifyMultipart);
  fastifyInstance.register(fastifyCookie);
  fastifyInstance.register(fastifySession, {
    secret: "passwordpasswordpasswordpassword",
  });
  await app.listen(configuration.server.port, "0.0.0.0");
}
bootstrap();
export { app };
